home *** CD-ROM | disk | FTP | other *** search
/ HyperLib 1997 Winter - Disc 1 / HYPERLIB-1997-Winter-CD1.ISO.7z / HYPERLIB-1997-Winter-CD1.ISO / オンラインウェア / PRG / hexedit107 folder.sit / hexedit107 folder / HexEditSource / Source / EditRoutines.c < prev    next >
Text File  |  1993-12-14  |  18KB  |  760 lines

  1. /*********************************************************************
  2.  * EditRoutines.c
  3.  *
  4.  * Implements a linked-list / virtual memory scheme for hex editing
  5.  *
  6.  * HexEdit, a simple hex editor
  7.  * copyright 1993, Jim Bumgardner
  8.  *********************************************************************/
  9.  
  10. #include "HexEdit.h"
  11.  
  12. EditChunk    **gScrapChunk;
  13. short        gScrapCount;
  14. UndoRecord    gUndoRec, gRedoRec;
  15.  
  16. void LoadFile(EditWindowPtr dWin);
  17. void UnloadFile(EditWindowPtr dWin);
  18. EditChunk **NewChunk(long size, long addr, long filePos, short type);
  19. EditChunk **AppendChunk(EditChunk **list, EditChunk **chunk);
  20. void LoadChunk(EditWindowPtr dWin, EditChunk **cc);
  21. void UnloadLeastUsedChunk(EditWindowPtr dWin);
  22. void UnloadChunk(EditWindowPtr dWin, EditChunk    **cc, Boolean writeFlag);
  23.  
  24. // Assumes window has just been opened, file is open, fileSize field is correct
  25. void LoadFile(EditWindowPtr dWin)
  26. {
  27.     EditChunk    **nc;
  28.     long        count,chunkSize,pos;
  29.     count = dWin->fileSize;
  30.     pos = 0L;
  31.     while (count) {
  32.         if (count <= (MaxFileRAM - SlushRAM))
  33.             chunkSize = count;
  34.         else
  35.             chunkSize = (MaxFileRAM - SlushRAM);
  36.         count -= chunkSize;
  37.         nc = NewChunk(chunkSize,pos,pos, CT_Original);
  38.         dWin->firstChunk = AppendChunk(dWin->firstChunk, nc);
  39.         pos += chunkSize;
  40.     }
  41.     dWin->curChunk = dWin->firstChunk;
  42. }
  43.  
  44. void DisposeChunk(EditWindowPtr dWin, EditChunk **cc)
  45. {
  46.     if ((*cc)->loaded) {
  47.         if ((*cc)->loaded)
  48.             UnloadChunk(dWin,cc, false);
  49.         DisposHandle((Handle) cc);
  50.     }
  51. }
  52.  
  53. void UnloadFile(EditWindowPtr dWin)
  54. {
  55.     EditChunk    **cc,**bc;
  56.     cc = dWin->firstChunk;
  57.     while (cc) {
  58.         bc = (*cc)->next;
  59.         DisposeChunk(dWin, cc);
  60.         cc = bc;
  61.     }
  62.     dWin->firstChunk = dWin->curChunk = NULL;
  63. }
  64.  
  65.  
  66. EditChunk **NewChunk(long size, long addr, long filePos, short type)
  67. {
  68.     EditChunk **nc;
  69.     nc = (EditChunk **) NewHandleClear(sizeof(EditChunk));
  70.     if (nc == NULL) {
  71.         ErrorAlert(ES_Caution, "Out of Memory");
  72.         return NULL;
  73.     }
  74.     (*nc)->type = type;
  75.     (*nc)->size = size;
  76.     (*nc)->addr = addr;
  77.     (*nc)->filePos = filePos;
  78.     (*nc)->lastCtr = -1;
  79.     if (type == CT_Unwritten) {
  80.         (*nc)->loaded = true;
  81.         (*nc)->allocSize = size;
  82.         (*nc)->data = NewHandleClear((*nc)->allocSize);
  83.         if ((*nc)->data == NULL) {
  84.             ErrorAlert(ES_Caution, "Out of Memory");
  85.             DisposHandle((Handle) nc);
  86.             return NULL;
  87.         }
  88.     }
  89.     else {
  90.         (*nc)->loaded = false;
  91.         (*nc)->data = NULL;
  92.         (*nc)->allocSize = 0L;
  93.     }
  94.     return nc;
  95. }
  96.  
  97. EditChunk **AppendChunk(EditChunk **list, EditChunk **chunk)
  98. {
  99.     if (list) {
  100.         register EditChunk    **curChunk;
  101.         curChunk = list;
  102.         while ((*curChunk)->next)
  103.             curChunk = (*curChunk)->next;
  104.         (*curChunk)->next = chunk;
  105.         (*chunk)->prev = curChunk;
  106.         (*chunk)->next = NULL;
  107.     }
  108.     else {
  109.         list = chunk;
  110.         (*chunk)->next = (*chunk)->prev = NULL;
  111.     }
  112.     return list;
  113. }
  114.  
  115. void SetCurrentChunk(EditWindowPtr dWin, long addr)
  116. {
  117.     register EditChunk **cc;
  118.     cc = GetChunkByAddr(dWin,addr);
  119.     dWin->curChunk = cc;
  120. }
  121.  
  122. EditChunk **GetChunkByAddr(EditWindowPtr dWin, long addr)
  123. {
  124.     register EditChunk **cc;
  125.     if (dWin->curChunk && addr >= (*dWin->curChunk)->addr)
  126.         cc = dWin->curChunk;
  127.     else    // Otherwise, start from beginning of chain
  128.         cc = dWin->firstChunk;
  129.     while (cc) {
  130.         if (addr < (*cc)->addr+(*cc)->size)
  131.             break;
  132.         else {
  133.             if ((*cc)->next)
  134.                 cc = (*cc)->next;
  135.             else
  136.                 return cc;
  137.         }
  138.     }
  139.     return cc;
  140. }
  141.  
  142. short GetByte(EditWindowPtr dWin, long addr)
  143. {
  144.     register EditChunk **cc;
  145.     if ((cc = GetChunkByAddr(dWin,addr)) != NULL) {
  146.         // Correct Chunk
  147.         if (!(*cc)->loaded)
  148.             LoadChunk(dWin,cc);
  149.         if ((*cc)->lastCtr != dWin->useCtr) {
  150.             // Update the Counter
  151.             ++dWin->useCtr;
  152.             (*cc)->lastCtr = dWin->useCtr;
  153.         }
  154.         return (unsigned char) (*(*cc)->data)[addr - (*cc)->addr];
  155.     }
  156.     return -1;
  157. }
  158.  
  159. void LoadChunk(EditWindowPtr dWin, EditChunk **cc)
  160. {
  161.     long    count;
  162.     OSErr    oe;
  163.     short    refNum;
  164.     if ((*cc)->loaded)
  165.         return;
  166.     // Check if we can fit within MaxFileRam, if not, deallocate old chunks
  167.     // until we're ok
  168.     while (dWin->totLoaded+(*cc)->size > MaxFileRAM) {
  169.         UnloadLeastUsedChunk(dWin);
  170.     }
  171.     (*cc)->data = NewHandleClear((*cc)->size);
  172.     if ((*cc)->data == NULL) {
  173.         ErrorAlert(ES_Caution,"Not enough memory");
  174.         (*cc)->allocSize = 0L;
  175.         (*cc)->loaded = false;
  176.     }
  177.     else {
  178.         if ((*cc)->type == CT_Work)
  179.             refNum = dWin->workRefNum;
  180.         else
  181.             refNum = dWin->refNum;
  182.         (*cc)->allocSize = (*cc)->size;
  183.         (*cc)->loaded = true;
  184.         if ((oe = SetFPos(refNum, fsFromStart, (*cc)->filePos)) != noErr)
  185.             OSErrorAlert(ES_Caution,"Seek Error",oe);
  186.         count = (*cc)->size;
  187.         dWin->totLoaded += (*cc)->size;
  188.         if ((oe = FSRead(refNum, &count, *(*cc)->data)) != noErr)
  189.             OSErrorAlert(ES_Caution,"Read Error",oe);
  190.     }
  191. }
  192.  
  193. void UnloadLeastUsedChunk(EditWindowPtr dWin)
  194. {
  195.     EditChunk    **cc,**oc=NULL;
  196.     oc = cc = dWin->firstChunk;
  197.     while (cc) {
  198.         if ((*cc)->loaded && 
  199.             (!(*oc)->loaded || (*cc)->lastCtr < (*oc)->lastCtr))
  200.             oc = cc;
  201.         cc = (*cc)->next;
  202.     }
  203.     if (oc)
  204.         UnloadChunk(dWin, oc, true);
  205. }
  206.  
  207. void UnloadChunk(EditWindowPtr dWin, EditChunk    **cc, Boolean writeFlag)
  208. {
  209.     long    count;
  210.     OSErr    oe;
  211.  
  212.     if (cc && (*cc)->loaded && (*cc)->data) {
  213.         if (writeFlag && (*cc)->type == CT_Unwritten) {
  214.             // Record New Chunks in Work File
  215.             oe = SetFPos(dWin->workRefNum, fsFromStart, dWin->workBytesWritten);
  216.             if (oe) {
  217.                 OSErrorAlert(ES_Caution, "SetFPos", oe);
  218.             }
  219.             count = (*cc)->size;
  220.             oe = FSWrite(dWin->workRefNum, &count, *(*cc)->data);
  221.             if (oe) {
  222.                 OSErrorAlert(ES_Caution, "FSWrite", oe);
  223.             }
  224.             (*cc)->type = CT_Work;
  225.             (*cc)->filePos = dWin->workBytesWritten;
  226.             dWin->workBytesWritten += count;
  227.         }
  228.  
  229.         dWin->totLoaded -= (*cc)->size;
  230.         (*cc)->loaded = false;
  231.         DisposHandle((*cc)->data);
  232.         (*cc)->data = NULL;
  233.         (*cc)->allocSize = 0L;
  234.     }
  235. }
  236.  
  237. void RewriteAddressChain(EditChunk **fc)
  238. {
  239.     EditChunk    **nc;
  240.     // Rewrite Addresses of chunks starting from fc
  241.     nc = (*fc)->next;
  242.     while (nc) {
  243.         (*nc)->addr = (*(*nc)->prev)->addr + (*(*nc)->prev)->size;
  244.         nc = (*nc)->next;
  245.     }
  246. }
  247.  
  248. void DeleteSelection(EditWindowPtr dWin)
  249. {
  250.     EditChunk **fc,**ec,**nc,**tc;
  251.  
  252.     if (dWin->endSel == dWin->startSel)
  253.         return;
  254.  
  255.     // Identify Starting Chunk
  256.     fc = GetChunkByAddr(dWin, dWin->startSel);
  257.     dWin->curChunk = fc;        // Optimize chunk searches
  258.  
  259.     // Identify Ending Chunk
  260.     ec = GetChunkByAddr(dWin, dWin->endSel);
  261.  
  262.     // If Chunks are the same
  263.     if (fc == ec) {
  264.         // If chunk is unwritten
  265.         if ((*fc)->type == CT_Unwritten) {
  266.             // Delete Chars from Buffer
  267.             // 12/14 JAB!!!  fixed editing bug
  268.             BlockMove(*(*fc)->data + (dWin->endSel - (*fc)->addr),
  269.                       *(*fc)->data + (dWin->startSel - (*fc)->addr),
  270.                       (*fc)->size - (dWin->endSel - (*fc)->addr));
  271. /*            BlockMove(*(*fc)->data + (dWin->endSel - (*fc)->addr),*/
  272. /*                      *(*fc)->data + (dWin->startSel - (*fc)->addr),*/
  273. /*                      dWin->endSel - dWin->startSel);*/
  274.             (*fc)->size -= dWin->endSel - dWin->startSel;
  275.         }
  276.         else {
  277.             UnloadChunk(dWin, fc, true);
  278.             // Split into two chunks
  279.             nc = NewChunk((*fc)->size - (dWin->endSel - (*fc)->addr), 
  280.                             0, 
  281.                             (*fc)->filePos + (dWin->endSel - (*fc)->addr), 
  282.                             (*fc)->type);
  283.             (*nc)->prev = fc;
  284.             (*nc)->next = (*fc)->next;
  285.             if ((*nc)->next)
  286.                 (*(*nc)->next)->prev = nc;
  287.             (*fc)->next = nc;
  288.             (*fc)->size = dWin->startSel - (*fc)->addr;
  289.         }
  290.     }
  291.     else {
  292.         // Truncate end of first Chunk
  293.         (*fc)->size = dWin->startSel - (*fc)->addr;
  294.         // Unlink & Dispose Middle Chunks, If Any
  295.         nc = (*fc)->next;
  296.         while (nc != ec) {
  297.             tc = (*nc)->next;
  298.             DisposeChunk(dWin, nc);
  299.             nc = tc;
  300.         }
  301.         (*ec)->prev = fc;
  302.         (*fc)->next = ec;
  303.         // Truncate beg of end chunk
  304.         if ((*ec)->type == CT_Unwritten) {
  305.             long    offset;
  306.             offset = dWin->endSel - (*ec)->addr;
  307.             BlockMove(*(*ec)->data, *(*ec)->data+offset, (*ec)->size - offset);
  308.             (*ec)->size -= offset;
  309.         }
  310.         else {
  311.             long    offset;
  312.             offset = dWin->endSel - (*ec)->addr;
  313.             UnloadChunk(dWin, ec, true);
  314.             (*ec)->filePos += offset;
  315.             (*ec)->size -= offset;
  316.         }
  317.     }
  318.  
  319.     dWin->fileSize -= (dWin->endSel - dWin->startSel);
  320.  
  321.     RewriteAddressChain(fc);
  322.  
  323.     // Modify Current Selection such that  endSel = firstSel
  324.     dWin->endSel = dWin->startSel;
  325.     dWin->dirtyFlag = true;
  326. }
  327.  
  328. // Assumes selection point is already 0 chars wide...
  329. void InsertCharacter(EditWindowPtr dWin, short charCode)
  330. {
  331.     EditChunk **fc,**ec,**nc,**tc;
  332.  
  333.     // !! Remember Current State for Undo
  334.  
  335.  
  336.     // Insert Character Into List
  337.     //    Identify current chunk - optimize so that if char is between
  338.     //        chunks, pick the unwritten one of the two...
  339.  
  340.     // Identify Starting Chunk
  341.     fc = GetChunkByAddr(dWin, dWin->startSel);
  342.  
  343.     //    Identify current chunk - optimize so that if char is between
  344.     //    chunks, pick the unwritten one of the two... - this way, if I keep typing
  345.     //  characters, I won't generate a bunch of 1 byte chunks.
  346.     if (dWin->startSel - (*fc)->addr == 0 &&
  347.         (*fc)->prev && (*fc)->type != CT_Unwritten &&
  348.         (*(*fc)->prev)->type == CT_Unwritten) {
  349.         fc = (*fc)->prev;
  350.     }
  351.     dWin->curChunk = fc;        // Optimize chunk searches
  352.  
  353.     //    If current chunk is not unwritten
  354.     if ((*fc)->type != CT_Unwritten) {
  355.         // Unload it
  356.         UnloadChunk(dWin, fc, true);
  357.  
  358.         if (dWin->startSel > (*fc)->addr) {
  359.  
  360.             // Split into two chunks
  361.             if (dWin->startSel < (*fc)->addr + (*fc)->size) {
  362.                 ec = NewChunk((*fc)->size - (dWin->startSel - (*fc)->addr), 
  363.                                 0, 
  364.                                 (*fc)->filePos + (dWin->startSel - (*fc)->addr), 
  365.                                 (*fc)->type);
  366.                 (*ec)->prev = fc;
  367.                 (*ec)->next = (*fc)->next;
  368.                 if ((*ec)->next)
  369.                     (*(*ec)->next)->prev = ec;
  370.                 (*fc)->next = ec;
  371.             }
  372.             else
  373.                 ec = (*fc)->next;
  374.  
  375.             (*fc)->size = dWin->startSel - (*fc)->addr;
  376.         }
  377.         else {
  378.             ec = fc;
  379.             fc = (*fc)->prev;
  380.         }
  381.  
  382.         // Add New unwritten chunk in middle with 0 size
  383.         nc = NewChunk(0,0,0,CT_Unwritten);
  384.         if (fc) {
  385.             (*fc)->next = nc;
  386.             (*nc)->addr = (*fc)->addr + (*fc)->size;
  387.         }
  388.         else
  389.             dWin->firstChunk = nc;
  390.         if (ec)
  391.             (*ec)->prev = nc;
  392.         (*nc)->prev = fc;
  393.         (*nc)->next = ec;
  394.         // current chunk = new chunk
  395.         dWin->curChunk = nc;
  396.         fc = nc;
  397.     }
  398.  
  399.     //    Expand Ptr if Necessary
  400.     if ((*fc)->allocSize <= (*fc)->size) {
  401.         (*fc)->allocSize += AllocIncr;        // !! consider expanding as size goes up
  402.         SetHandleSize((*fc)->data,(*fc)->allocSize);
  403.     }
  404.  
  405.     // Make Room for Character if necessary
  406.     if (dWin->startSel < (*fc)->addr + (*fc)->size)
  407.         BlockMove(*(*fc)->data + (dWin->startSel - (*fc)->addr), 
  408.                   *(*fc)->data + (1+(dWin->startSel - (*fc)->addr)), 
  409.                   (*fc)->addr + (*fc)->size - dWin->startSel);
  410.  
  411.     //    Insert Char into buffer
  412.     (*(*fc)->data)[dWin->startSel - (*fc)->addr] = charCode;
  413.  
  414.     //    Update Fields in this chunk
  415.     (*fc)->size++;
  416.     dWin->fileSize++;
  417.  
  418.     // Set Dirty Flag
  419.     dWin->dirtyFlag = true;
  420.  
  421.     //    Update addr fields of following chunks
  422.     RewriteAddressChain(fc);
  423.  
  424.     // Increment current Selection
  425.     dWin->startSel++;
  426.     dWin->endSel++;
  427.  
  428.  
  429.     // Update Display
  430.     ScrollToSelection(dWin,dWin->startSel,true, false);
  431. }
  432.  
  433. void ReleaseEditScrap(EditWindowPtr dWin, EditChunk ***scrap)
  434. {
  435.     EditChunk    **cc,**bc;
  436.     cc = *scrap;
  437.     while (cc) {
  438.         bc = (*cc)->next;
  439.         DisposeChunk(dWin, cc);
  440.         cc = bc;
  441.     }
  442.     *scrap = NULL;
  443. }
  444.  
  445. // High Level Copy
  446. void CopySelection(EditWindowPtr dWin)
  447. {
  448.     CopyOperation(dWin, &gScrapChunk);
  449.     if (gScrapChunk) {
  450.         // Copy to Desk Scrap
  451.         ZeroScrap();
  452.         HLock((*gScrapChunk)->data);
  453.         PutScrap((*gScrapChunk)->size, 'TEXT', *(*gScrapChunk)->data);
  454.         HUnlock((*gScrapChunk)->data);
  455.         gScrapCount = ScrapInfo.scrapCount;
  456.         (*gScrapChunk)->lastCtr = 0;    // Flag as internal
  457.     }
  458. }
  459.  
  460.  
  461.  
  462. void CopyOperation(EditWindowPtr dWin, EditChunk ***scrapChunk)
  463. {
  464.     EditChunk    **fc,**ec,**nc,**tc;
  465.     // Unload current scrap
  466.     ReleaseEditScrap(dWin, scrapChunk);
  467.  
  468.     // Copy current selection into scrapChunk
  469.     // Identify Starting Chunk
  470.     fc = GetChunkByAddr(dWin, dWin->startSel);
  471.     dWin->curChunk = fc;        // Optimize chunk searches
  472.  
  473.     // Identify Ending Chunk
  474.     ec = GetChunkByAddr(dWin, dWin->endSel);
  475.  
  476.     // If Chunks are the same
  477.     nc = NewChunk(dWin->endSel - dWin->startSel,
  478.                   0,
  479.                   0,
  480.                   CT_Unwritten);
  481.     if (nc == NULL)
  482.         return;
  483.  
  484.     *scrapChunk = nc;
  485.  
  486.     if (fc == ec) {
  487.         LoadChunk(dWin, fc);
  488.         BlockMove(*(*fc)->data + (dWin->startSel - (*fc)->addr),
  489.                   *(*nc)->data,
  490.                   (*nc)->size);
  491.     }
  492.     else {
  493.         // First Chunk to End
  494.         tc = fc;
  495.         LoadChunk(dWin, tc);
  496.         BlockMove(*(*tc)->data + (dWin->startSel - (*tc)->addr),
  497.                   *(*nc)->data,
  498.                   (*tc)->size - (dWin->startSel - (*tc)->addr));
  499.         tc = (*tc)->next;
  500.  
  501.         // Middle Chunks, If Any
  502.         while (tc != ec) {
  503.             LoadChunk(dWin, tc);
  504.             BlockMove(*(*tc)->data,
  505.                       *(*nc)->data + ((*tc)->addr - dWin->startSel),
  506.                       (*tc)->size);
  507.             tc = (*tc)->next;
  508.         }
  509.  
  510.         // Last Chunk
  511.         LoadChunk(dWin, tc);
  512.         BlockMove(*(*tc)->data,
  513.                   *(*nc)->data + ((*tc)->addr - dWin->startSel),
  514.                   dWin->endSel - (*tc)->addr);
  515.     }
  516. }
  517.  
  518. void CutSelection(EditWindowPtr dWin)
  519. {
  520.     RememberOperation(dWin, EO_Cut, &gUndoRec);
  521.     CopyOperation(dWin, &gScrapChunk);        // Copy into paste buffer
  522.     DeleteSelection(dWin);
  523.     dWin->dirtyFlag = true;
  524.     ScrollToSelection(dWin,dWin->startSel,true, false);
  525. }
  526.  
  527. // High Level Paste
  528. void PasteSelection(EditWindowPtr dWin)
  529. {
  530.     RememberOperation(dWin, EO_Paste, &gUndoRec);
  531.     PasteOperation(dWin, gScrapChunk);
  532.     dWin->dirtyFlag = true;
  533.     ScrollToSelection(dWin,dWin->startSel,true, false);
  534. }
  535.  
  536. Boolean HexConvertScrap(EditWindowPtr dWin, EditChunk **scrapChunk)
  537. {
  538.     Handle    rh=NULL;
  539.     Ptr        sp,dp,esp;
  540.     short    val;
  541.     Boolean    loFlag;
  542.  
  543.     rh = NewHandle((*scrapChunk)->size);
  544.     if (rh == NULL) {
  545.         ErrorAlert(ES_Caution, "Not enough memory");
  546.         return false;
  547.     }
  548.     HLock(rh);
  549.     HLock((*scrapChunk)->data);
  550.     sp = *(*scrapChunk)->data;
  551.     esp = sp + (*scrapChunk)->size;
  552.     dp = *rh;
  553.     loFlag = false;
  554.     for (; sp < esp; ++sp) {
  555.         if (*sp == '0' && *(sp+1) == 'x') {
  556.             loFlag = 0;
  557.             ++sp;
  558.             continue;
  559.         }
  560.         if (isspace(*sp) || ispunct(*sp)) {
  561.             loFlag = 0;
  562.             continue;
  563.         }
  564.         if (*sp >= '0' && *sp <= '9')
  565.             val = *sp - '0';
  566.         else if (*sp >= 'A' && *sp <= 'F')
  567.             val = 0x0A + (*sp - 'A');
  568.         else if (*sp >= 'a' && *sp <= 'f')
  569.             val = 0x0A + (*sp - 'a');
  570.         else
  571.             goto HexError;
  572.         if (loFlag) {
  573.             *(dp-1) = (*(dp-1) << 4) | val;
  574.             loFlag = 0;
  575.         }            
  576.         else {
  577.             *dp = val;
  578.             ++dp;
  579.             loFlag = 1;
  580.         }
  581.     }
  582.     if (dp - *rh == 0)
  583.         goto HexError;
  584.     (*scrapChunk)->size = dp - *rh;
  585.     HUnlock(rh);
  586.     HUnlock((*scrapChunk)->data);
  587.     BlockMove(*rh, *(*scrapChunk)->data, (*scrapChunk)->size);
  588.     DisposHandle(rh);
  589.     (*scrapChunk)->lastCtr = 0;        // Mark as Internal
  590.     return true;
  591. HexError:
  592.     HUnlock(rh);
  593.     HUnlock((*scrapChunk)->data);
  594.     ErrorAlert(ES_Caution, "Only valid Hex values may be pasted here");
  595.     DisposHandle(rh);
  596.     return false;
  597. }
  598.  
  599. void PasteOperation(EditWindowPtr dWin, EditChunk **scrapChunk)
  600. {
  601.     EditChunk **fc,**ec,**nc;
  602.  
  603.     // Hex Pasting Mode for Outside Pastes
  604.     if (dWin->editMode == EM_Hex && (*gScrapChunk)->lastCtr == 1) {
  605.         if (!HexConvertScrap(dWin,scrapChunk))
  606.             return;
  607.     }
  608.  
  609.     // Create duplicate scrap attached to nc->nec
  610.     nc = NewChunk((*scrapChunk)->size,
  611.                     0,
  612.                     0,
  613.                     CT_Unwritten);
  614.     if (nc == NULL)
  615.         return;
  616.  
  617.     BlockMove(*(*scrapChunk)->data,
  618.               *(*nc)->data,
  619.               (*nc)->size);
  620.  
  621.     DeleteSelection(dWin);
  622.     // Insert paste buffer into selStart
  623.  
  624.     fc = GetChunkByAddr(dWin, dWin->startSel);
  625.     if ((*fc)->addr < dWin->startSel) {
  626.         // Split 'em up
  627.         // Unload it
  628.         UnloadChunk(dWin, fc, true);
  629.  
  630.         // Split into two chunks
  631.         if (dWin->startSel < (*fc)->addr + (*fc)->size) {
  632.             ec = NewChunk((*fc)->size - (dWin->startSel - (*fc)->addr), 
  633.                             0, 
  634.                             (*fc)->filePos + (dWin->startSel - (*fc)->addr), 
  635.                             (*fc)->type);
  636.             (*ec)->prev = fc;
  637.             (*ec)->next = (*fc)->next;
  638.             if ((*ec)->next)
  639.                 (*(*ec)->next)->prev = ec;
  640.         }
  641.         else
  642.             ec = (*fc)->next;
  643.  
  644.         (*fc)->next = ec;
  645.         (*fc)->size = dWin->startSel - (*fc)->addr;
  646.     }
  647.     else {
  648.         ec = fc;
  649.         fc = (*fc)->prev;
  650.     }
  651.  
  652.     // Insert fc->nc->ec
  653.     if (fc) {
  654.         (*fc)->next = nc;
  655.         (*nc)->prev = fc;
  656.         (*nc)->addr = (*fc)->addr + (*fc)->size;
  657.     }
  658.     else {
  659.         dWin->firstChunk = nc;
  660.         (*nc)->addr = 0L;
  661.     }
  662.  
  663.     if (ec) {
  664.         (*nc)->next = ec;
  665.         (*ec)->prev = nc;
  666.     }
  667.  
  668.     // Correct addresses
  669.     RewriteAddressChain(nc);
  670.  
  671.     // Reset Selection
  672.     dWin->startSel = dWin->endSel = (*nc)->addr + (*nc)->size;
  673.  
  674.     // Update other stuff
  675.     dWin->fileSize += (*scrapChunk)->size;
  676.     dWin->dirtyFlag = true;
  677. }
  678.  
  679. void ClearSelection(EditWindowPtr dWin)
  680. {
  681.     RememberOperation(dWin, EO_Clear, &gUndoRec);
  682.     DeleteSelection(dWin);
  683.     dWin->dirtyFlag = true;
  684.     ScrollToSelection(dWin, dWin->startSel, true, false);
  685. }
  686.  
  687. // Remember current state for Undo of following operation
  688. void RememberOperation(EditWindowPtr dWin, short opType, UndoRecord *ur)
  689. {
  690.     // Forget Last stuff
  691.     if (ur == &gRedoRec) {
  692.         // Reset menu text to Redo
  693.         Str31    menuStr;
  694.         GetItem(gEditMenu, EM_Undo, menuStr);
  695.         if (menuStr[1] == 'R')
  696.             BlockMove("Un", &menuStr[1], 2);
  697.         else
  698.             BlockMove("Re", &menuStr[1], 2);
  699.         SetItem(gEditMenu, EM_Undo, menuStr);
  700.     }
  701.     else {
  702.         Str31    undoStr;
  703.         Str31    menuStr;
  704.         GetIndString(undoStr, UndoSTRs, opType);
  705.         BlockMove("¥pUndo ", menuStr, 6);
  706.         BlockMove(&undoStr[1], &menuStr[6], undoStr[0]);
  707.         menuStr[0] += undoStr[0];
  708.         SetItem(gEditMenu, EM_Undo, menuStr);
  709.     }
  710.  
  711.     ReleaseEditScrap(dWin, &ur->undoScrap);
  712.     // Clear Undo Stuff
  713.     ur->undoScrap = NULL;
  714.     ur->type = opType;
  715.     ur->startSel = dWin->startSel;
  716.     ur->endSel = dWin->endSel;
  717.     ur->fileSize = dWin->fileSize;
  718.     ur->window = dWin;
  719.     CopyOperation(dWin, &ur->undoScrap);
  720.     (*ur->undoScrap)->lastCtr= 0;
  721.     dWin->lastTypePos = -1;    // Clear Special Editing Modes
  722.     dWin->loByteFlag = false;
  723. }
  724.  
  725. void UndoOperation()
  726. {
  727.     EditWindowPtr    dWin = gUndoRec.window;
  728.  
  729.     if (gUndoRec.type == 0)
  730.         return;
  731.     if (dWin != (EditWindowPtr) FrontWindow())
  732.         SelectWindow((WindowPtr) dWin);
  733.  
  734.     switch (gUndoRec.type) {
  735.     case EO_Typing:
  736.     case EO_Paste:
  737.     case EO_Insert:
  738.         dWin->startSel = gUndoRec.startSel;
  739.         dWin->endSel = dWin->fileSize - (gUndoRec.fileSize - gUndoRec.endSel);
  740.         RememberOperation(dWin, EO_Delete, &gRedoRec);
  741.         DeleteSelection(dWin);
  742.         PasteOperation(dWin, gUndoRec.undoScrap);
  743.         break;
  744.     case EO_Cut:
  745.     case EO_Clear:
  746.     case EO_Delete:
  747.         dWin->startSel = dWin->endSel = gUndoRec.startSel;
  748.         RememberOperation(dWin, EO_Insert, &gRedoRec);
  749.         PasteOperation(dWin, gUndoRec.undoScrap);
  750.         break;
  751.     }
  752.  
  753.     ReleaseEditScrap(dWin, &gUndoRec.undoScrap);
  754.     gUndoRec = gRedoRec;
  755.     gRedoRec.undoScrap = NULL;
  756.  
  757.     ScrollToSelection(dWin,dWin->startSel,true, false);
  758. }
  759.  
  760.